home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 25 / Cream of the Crop 25.iso / os2 / gnuwget.zip / wget-1.4.3 / src / ftp-ls.c < prev    next >
C/C++ Source or Header  |  1997-02-08  |  11KB  |  411 lines

  1. /* Dealing with Unix FTP servers.
  2.    Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
  3.    
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2 of the License, or
  7.    (at your option) any later version.
  8.    
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.    
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18.  
  19. #ifdef HAVE_CONFIG_H
  20. #  include <config.h>
  21. #endif
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #ifdef HAVE_STRING_H
  25. #  include <string.h>
  26. #else
  27. #  include <strings.h>
  28. #endif
  29. #ifdef HAVE_UNISTD_H
  30. #  include <unistd.h>
  31. #endif
  32. #include <sys/types.h>
  33. #include <ctype.h>
  34. #include <errno.h>
  35. #include <assert.h>
  36.  
  37. #include "wget.h"
  38. #include "url.h"
  39. #include "utils.h"
  40. #include "ftp.h"
  41. #include "options.h"
  42.  
  43. extern struct options opt;
  44.  
  45. /* Converts symbolic permissions to number-style ones, e.g. string
  46.    rwxr-xr-x to 755. For now, it knows nothing of
  47.    setuid/setgid/sticky. ACLs are ignored. */
  48. int
  49. symperms(const char *s)
  50. {
  51.    int perms = 0, i;
  52.    
  53.    if (strlen(s) < 9)
  54.       return 0;
  55.    for (i = 0; i < 3; i++, s += 3)
  56.    {
  57.       perms <<= 3;
  58.       perms += (((*s == 'r') << 2) + ((s[1] == 'w') << 1) +
  59.         (s[2] == 'x' || s[2] == 's'));
  60.    }
  61.    return perms;
  62. }
  63.  
  64. /* We support only Unix FTP servers for now. */
  65. struct fileinfo *
  66. ftp_parse_ls(const char *file)
  67. {
  68.    return ftp_parse_unix_ls(file);
  69. }
  70.  
  71. /* The function parses the Un*x-ish style directory listing stored in
  72.    file, and returns the linked list of fileinfo (system-independent)
  73.    entries. The file is considered to be produced by the standard Unix
  74.    ls -la output (whatever that meant:). BSD (no group) and SYSV (with
  75.    group) listings are handled.
  76.  
  77.    The time stamps are stored in a separate variable, time_t
  78.    compatible (I hope). The time-zones are ignored (unfortunately). */
  79. struct fileinfo *
  80. ftp_parse_unix_ls(const char *file)
  81. {
  82.    FILE *fp;
  83.    static const char *months[] = {
  84.       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  85.       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  86.    };
  87.    int next, len, i, error, ignore;
  88.    int year, month, day;        /* For time analysis.  */
  89.    int hour, min, sec;
  90.    struct tm timestruct, *tnow;
  91.    time_t timenow;
  92.    
  93.    char *line, *tok;            /* Tokenizer. */
  94.    struct fileinfo *dir, *l, cur; /* List creation. */
  95.  
  96.    fp = fopen(file, "r");
  97.    if (!fp)
  98.    {
  99.       fprintf(opt.lfile, "%s: %s\n", file, mystrerror(errno));
  100.       return NULL;
  101.    }
  102.    dir = l = NULL;
  103.  
  104.    /* Line loop to end of file: */
  105.    while ((line = read_whole_line(fp)))
  106.    {
  107. #ifdef DEBUG
  108.       if (opt.debug)
  109.      fprintf(opt.lfile, "%s\n", line);
  110. #endif
  111.       len = strlen(line);
  112.       /* Destroy <CR> if there is one. */
  113.       if (len && line[len - 1] == '\r')
  114.      line[--len] = '\0';
  115.  
  116.       /* Skip if total... */
  117.       if (!strncasecmp(line, "total", 5))
  118.       {
  119.      free(line);
  120.      continue;
  121.       }
  122.       /* Get the first token (permissions). */
  123.       tok = strtok(line, " ");
  124.       if (!tok)
  125.       {
  126.      free(line);
  127.      continue;
  128.       }
  129.  
  130.       cur.name = NULL;
  131.       cur.linkto = NULL;
  132.  
  133.       /* Decide whether we deal with a file or a directory. */
  134.       switch (*tok)
  135.       {
  136.      case '-':
  137.         cur.type = PLAINFILE;
  138.         DEBUGP("PLAINFILE; ");
  139.         break;
  140.      case 'd':
  141.         cur.type = DIRECTORY;
  142.         DEBUGP("DIRECTORY; ");
  143.         break;
  144.      case 'l':
  145.         cur.type = SYMLINK;
  146.         DEBUGP("SYMLINK; ");
  147.         break;
  148.      default:
  149.         cur.type = UNKNOWN;
  150.         DEBUGP("UNKOWN; ");
  151.         break;
  152.       }
  153.  
  154.       cur.perms = symperms(tok + 1);
  155. #ifdef DEBUG
  156.       if (opt.debug)
  157.      fprintf(opt.lfile, "perms %0o; ", cur.perms);
  158. #endif
  159.       
  160.       error = ignore = 0;       /* Errnoeous and ignoring entries are
  161.                    treated equally for now. */
  162.       year = hour = min = sec = 0; /* Silence the compiler. */
  163.       month = day = 0;
  164.       next = -1;
  165.       /* While there are tokens on the line, parse them.  Next is the
  166.      number of tokens left until the filename.
  167.  
  168.      I use the month-name token as the "anchor" (the place I
  169.      "know" the position wrt the file name).  When a month name is
  170.      encountered, next is set to 5.  Also, the preceding
  171.      characters are parsed to get the file size.
  172.  
  173.      This tactic is quite dubious when it comes to
  174.      internationalization issues (non-English month names), but it
  175.      works for now.  */
  176.       while ((tok = strtok(NULL, " ")))
  177.       {
  178.      --next;
  179.      if (next < 0)          /* I.e. a month name was not
  180.                    encountered. */
  181.      {
  182.         for (i = 0; i < 12; i++)
  183.            if (!strcmp(tok, months[i]))
  184.           break;
  185.         /* If we got a month, it means the token before it is the
  186.            size, and the filename is three tokens away. */
  187.         if (i != 12)
  188.         {
  189.            char *t = tok - 2;
  190.            long mul = 1;
  191.            
  192.            for (cur.size = 0; t > line && isdigit(*t); mul *= 10, t--)
  193.           cur.size += mul * (*t - '0');
  194.            if (t == line)
  195.            {
  196.           /* Something is seriously wrong. */
  197.           error = 1;
  198.           break;
  199.            }
  200.            month = i;
  201.            next = 5;
  202. #ifdef DEBUG
  203.            if (opt.debug)
  204.           fprintf(opt.lfile, "month: %s; ", months[month]);
  205. #endif
  206.         }
  207.      }
  208.      else if (next == 4)    /* Days */
  209.      {
  210.         if (tok[1])         /* Two-digit... */
  211.            day = 10 * (*tok - '0') + tok[1] - '0';
  212.         else                /* ...or one-digit. */
  213.            day = *tok - '0';
  214. #ifdef DEBUG
  215.         if (opt.debug)
  216.            fprintf(opt.lfile, "day: %d; ", day);
  217. #endif
  218.      }
  219.      else if (next == 3)
  220.      {
  221.         /* This ought to be either the time, or the year.  Let's
  222.            be flexible!
  223.  
  224.            If we have a number x, it's a year.  If we have x:y,
  225.            it's hours and minutes.  If we have x:y:z, z are
  226.            seconds.  */
  227.         year = 0;
  228.         min = hour = sec = 0;
  229.         /* We must deal with digits. */
  230.         if (isdigit(*tok))
  231.         {
  232.            /* Suppose it's year. */
  233.            for (; isdigit(*tok); tok++)
  234.           year = (*tok - '0') + 10 * year;
  235.            if (*tok == ':')
  236.            {
  237.           /* This means these were hours! */
  238.           hour = year;
  239.           year = 0;
  240.           ++tok;
  241.           /* Get the minutes. */
  242.           for (; isdigit(*tok); tok++)
  243.              min = (*tok - '0') + 10 * min;
  244.           if (*tok == ':')
  245.           {
  246.              /* The seconds too. */
  247.              ++tok;
  248.              for (; isdigit(*tok); tok++)
  249.             sec = (*tok - '0') + 10 * sec;
  250.           }
  251.            }
  252.         } /* isdigit(*tok) */
  253. #ifdef DEBUG
  254.         if (opt.debug)
  255.         {
  256.            if (year)
  257.           fprintf(opt.lfile, "year: %d (no tm); ", year);
  258.            else
  259.           fprintf(opt.lfile, "time: %02d:%02d:%02d (no yr); ", hour, min, sec);
  260.         }
  261. #endif
  262.      } /* next == 3 */
  263.      else if (next == 2)    /* The file name */
  264.      {
  265.         int fnlen;
  266.         char *p;
  267.  
  268.         /* Since the file name may contain a SPC, it is possible
  269.                for strtok to handle it wrong.  */
  270.         fnlen = strlen(tok);
  271.         if (fnlen < len - (tok - line))
  272.         {
  273.            /* So we have a SPC in the file name.  Restore the
  274.                   original.  */
  275.            tok[fnlen] = ' ';
  276.            /* If the file is a symbolic link, it should have a
  277.           ` -> ' somewhere.  */
  278.            if (cur.type == SYMLINK)
  279.            {
  280.           p = strstr(tok, " -> ");
  281.           if (!p)
  282.           {
  283.              error = 1;
  284.              break;
  285.           }
  286.           cur.linkto = nstrdup(p + 4);
  287. #ifdef DEBUG
  288.           if (opt.debug)
  289.              fprintf(opt.lfile, "link to: %s\n", cur.linkto);
  290. #endif
  291.           /* And separate it from the file name. */
  292.           *p = '\0';
  293.            }
  294.         }
  295.         /* If we have the filename, add it to the list of files or
  296.            directories. */
  297.         /* "." and ".." are an exception! */
  298.         if (!strcmp(tok, ".") || !strcmp(tok, ".."))
  299.         {
  300.            DEBUGP("\nIgnoring `.' and `..'; ");
  301.            ignore = 1;
  302.            break;
  303.         }
  304.         /* Some FTP sites choose to have ls -F as their default
  305.            LIST output, which marks the symlinks with a trailing
  306.            '@', directory names with a trailing '/' and
  307.            executables with a trailing '*'. This is no problem
  308.            unless encountering a symbolic link ending with '@', or
  309.            an executable ending with '*' on a server without
  310.            default -F output.  I believe these cases are very
  311.            rare.  */
  312.         fnlen = strlen(tok);    /* Re-calculate fnlen.  */
  313.         cur.name = (char *)nmalloc(fnlen + 1);
  314.         memcpy(cur.name, tok, fnlen + 1);
  315.         if (fnlen)
  316.         {
  317.            if (cur.type == DIRECTORY && cur.name[fnlen - 1] == '/')
  318.            {
  319.           cur.name[fnlen - 1] = '\0';
  320.           DEBUGP("trailing `/' on dir.\n");
  321.            }
  322.            else if (cur.type == SYMLINK && cur.name[fnlen - 1] == '@')
  323.            {
  324.           cur.name[fnlen - 1] = '\0';
  325.           DEBUGP("trailing `@' on link.\n");
  326.            }
  327.            else if (cur.type == PLAINFILE
  328.             && (cur.perms & 0111)
  329.             && cur.name[fnlen - 1] == '*')
  330.            {
  331.           cur.name[fnlen - 1] = '\0';
  332.           DEBUGP("trailing `*' on exec.\n");
  333.            }
  334.         } /* if (fnlen) */
  335.         else
  336.            error = 1;
  337.         break;
  338.      } /* next == 2 */
  339.      else
  340.         assert(0);
  341.       }    /* while */
  342.  
  343.       if (!cur.name || (cur.type == SYMLINK && !cur.linkto))
  344.      error = 1;
  345.       
  346.       DEBUGP("\n");
  347.  
  348.       if (error || ignore)
  349.       {
  350.      DEBUGP("Skipping.\n");
  351.      if (cur.name)
  352.         free(cur.name);
  353.      if (cur.linkto)
  354.         free(cur.linkto);
  355.      free(line);
  356.      continue;
  357.       }
  358.       
  359.       if (!dir)
  360.       {
  361.      l = dir = (struct fileinfo *)nmalloc(sizeof(struct fileinfo));
  362.      memcpy(l, &cur, sizeof(cur));
  363.      l->prev = l->next = NULL;
  364.       }
  365.       else
  366.       {
  367.      cur.prev = l;
  368.      l->next = (struct fileinfo *)nmalloc(sizeof(struct fileinfo));
  369.      l = l->next;
  370.      memcpy(l, &cur, sizeof(cur));
  371.      l->next = NULL;
  372.       }
  373.       /* Get the current time. */
  374.       timenow = time(NULL);
  375.       tnow = localtime(&timenow);
  376.       /* Build the time-stamp (the idea by zaga). */
  377.       timestruct.tm_sec   = sec;
  378.       timestruct.tm_min   = min;
  379.       timestruct.tm_hour  = hour;
  380.       timestruct.tm_mday  = day;
  381.       timestruct.tm_mon   = month;
  382.       if (year == 0)
  383.       {
  384.      /* Some listings will not specify the year if it is "obvious"
  385.         that the file was from the previous year.  E.g. if today
  386.         is 97-01-12, and you see a file of Dec 15th, its year is
  387.         1996, not 1997.  Thanks to Vladimir Volovich for
  388.         mentioning this!  */
  389.      if (month > tnow->tm_mon)
  390.         timestruct.tm_year = tnow->tm_year - 1;
  391.      else
  392.         timestruct.tm_year = tnow->tm_year;
  393.       }
  394.       else
  395.      timestruct.tm_year = year;
  396.       if (timestruct.tm_year >= 1900)
  397.      timestruct.tm_year -= 1900;
  398.       timestruct.tm_wday  = 0;
  399.       timestruct.tm_yday  = 0;
  400.       timestruct.tm_isdst = -1;
  401.       l->tstamp = mktime(×truct); /* Store the time-stamp. */
  402.  
  403.       /* Free the line. */
  404.       free(line);
  405.    }
  406.    /* Close the file. */
  407.    fclose(fp);
  408.    return dir;
  409. }
  410.  
  411.